/*
 * Copyright (C) 2015 Edward Raff
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.jstarcraft.ai.jsat.distributions.discrete;

/**
 * The discrete uniform distribution.
 * 
 * @author Edward Raff
 */
public class UniformDiscrete extends DiscreteDistribution {
    private int min;
    private int max;

    /**
     * Creates a new Uniform distribution with a min of 0 and a max of 10
     */
    public UniformDiscrete() {
        this(0, 10);
    }

    /**
     * Creates a new discrete uniform distribution
     * 
     * @param min the minimum value to occur
     * @param max the maximum value to occur
     */
    public UniformDiscrete(int min, int max) {
        setMinMax(min, max);
    }

    /**
     * Sets the minimum and maximum values at the same time, this is useful if
     * setting them one at a time may have caused a conflict with the previous
     * values
     *
     * @param min the new minimum value to occur
     * @param max the new maximum value to occur
     */
    public void setMinMax(int min, int max) {
        if (min >= max)
            throw new IllegalArgumentException("The input minimum (" + min + ") must be less than the given max (" + max + ")");
        this.min = min;
        this.max = max;
    }

    /**
     * Sets the minimum value to occur from the distribution, must be less than
     * {@link #getMax() }.
     *
     * @param min the minimum value to occur
     */
    public void setMin(int min) {
        if (min >= max)
            throw new IllegalArgumentException(min + " must be less than the max value " + max);
        this.min = min;
    }

    public int getMin() {
        return min;
    }

    /**
     * Sets the maximum value to occur from the distribution, must be greater than
     * {@link #getMin() }.
     * 
     * @param max the maximum value to occur
     */
    public void setMax(int max) {
        if (max <= min)
            throw new IllegalArgumentException(max + " must be greater than the min value " + min);
        this.max = max;
    }

    public int getMax() {
        return max;
    }

    @Override
    public double pmf(int x) {
        if (x < min || x > max)
            return 0;
        else
            return 1.0 / (1 + max - min);
    }

    @Override
    public double cdf(int x) {
        if (x >= max)
            return 1;
        else if (x < min)
            return 0;
        else
            return (1 - min + x) / (double) (1 + max - min);
    }

    @Override
    public double invCdf(double p) {
        if (p <= 0)
            return min;
        else if (p >= 1)
            return max;
        else
            return Math.max(1, Math.ceil((1 + max - min) * p) + min - 1);
    }

    @Override
    public double mean() {
        return max / 2.0 + min / 2.0;
    }

    @Override
    public double median() {
        return Math.floor(mean());
    }

    @Override
    public double mode() {
        return Double.NaN;
    }

    @Override
    public double variance() {
        long dif = (max - min + 1);
        dif *= dif;
        return (dif - 1) / 12.0;
    }

    @Override
    public double skewness() {
        return 0;
    }

    @Override
    public double min() {
        return min;
    }

    @Override
    public double max() {
        return max;
    }

    @Override
    public DiscreteDistribution clone() {
        return new UniformDiscrete(min, max);
    }

}
